#include "LecteurPhraseAvecArbre.h"
#include <stdlib.h>
#include "ErreurSyntaxique.h"
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst(ls.getLigne(), ls.getColonne());
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="si" || ls.getSymCour()=="tantque" || ls.getSymCour()=="repeter" || ls.getSymCour() == "pour" || ls.getSymCour()=="lire" || ls.getSymCour()=="ecrire" || ls.getSymCour()=="choisir");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instRepeter> | <instTq> | <instSi> | | <instLire> | <instEcrire>
	if(ls.getSymCour()=="tantque") {
		return instTq();
	} else if(ls.getSymCour()=="repeter") {
		return instRepeter();
	} else if(ls.getSymCour()=="pour") {
		return instPour();
	} else if(ls.getSymCour()=="si") {
		return instSi();
	} else if(ls.getSymCour()=="lire") {
		return instLire();
	} else if(ls.getSymCour()=="ecrire") {
		return instEcrire();
	} else if(ls.getSymCour()=="choisir") {
		return instChoisir();
	} else {
		return affectation();
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(ls.getLigne(), ls.getColonne(), var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi() {
//<instSi> ::= si ( <expEt> ) <seqInst> { sinonsi ( <expEt> ) <seqInst> } [ sinon <seqInst> ] finsi
	bool si;
	if(ls.getSymCour() == "si") {
		sauterSymCour("si");
		si = true;
	} else if(ls.getSymCour() == "sinonsi") {
		sauterSymCour("sinonsi");
		si = false;
	} else {
		erreur("<instSi>");
		return NULL;
	}

	if (ls.getSymCour()=="(") {
		ls.suivant();

		Noeud* nExpEt = expEt();
	
		sauterSymCour(")");
	
		Noeud * nExpSeq = seqInst();
	
		Noeud* suivant = NULL;
		if(ls.getSymCour() == "sinonsi") {
			suivant = instSi();
			return (new NoeudSi(ls.getLigne(), ls.getColonne(), nExpEt, nExpSeq, suivant, si));
		} else if (ls.getSymCour() == "sinon") {
			sauterSymCour("sinon");
			suivant = seqInst();
		}
	
		if(ls.getSymCour()=="finsi"){
			sauterSymCour("finsi");
			return (new NoeudSi(ls.getLigne(), ls.getColonne(), nExpEt, nExpSeq, suivant, si));
		} else {
			erreur("<intFinsi>");
			return NULL;
		}
	} else {
		erreur("<instSi/Sinonsi>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq() {
// <instTq> ::= tantque ( <expEt> ) <seqInst> fintantque
	sauterSymCour("tantque");
	if (ls.getSymCour()=="(") {
		ls.suivant();
		Noeud* nExpEt = expEt();
		sauterSymCour(")");
		
		Noeud* nSeqInst = seqInst();

		// fintantque
		if(ls.getSymCour()=="fintantque"){
			sauterSymCour("fintantque");
		} else {
			erreur("<finTq>");
		}
		return (new NoeudBoucle(ls.getLigne(), ls.getColonne(), Symbole("tantque"), nExpEt, nSeqInst));
	} else {
		erreur("<instTq>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter() {
// <instRepeter> ::= repeter <seqInst> jusqua ( <expEt> )
	sauterSymCour("repeter");
	
	Noeud* nSeqInst = seqInst();

	// jusqua
	if(ls.getSymCour()=="jusqua"){
		sauterSymCour("jusqua");

		if (ls.getSymCour()=="(") {
			ls.suivant();
			
			Noeud* nExpEt = expEt();
			sauterSymCour(")");
			return (new NoeudBoucle(ls.getLigne(), ls.getColonne(), Symbole("repeter"), nExpEt, nSeqInst));
		} else {
			erreur("<instJusqua>");
			return NULL;
		}

	} else {
		erreur("<instRepeter>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour() {
// <instPour> ::= pour (<affectation> ; <expEt> ; <affectation> ) <seqInst> finpour
	
	sauterSymCour("pour");

	if(ls.getSymCour()=="("){
		ls.suivant();

		Noeud* aff = affectation();
		sauterSymCour(";");

		Noeud* nExpEt = expEt();
		sauterSymCour(";");
		Noeud* incr = affectation();
		sauterSymCour(")");

		Noeud* nSeqInst = seqInst();

		if(ls.getSymCour()=="finpour"){
	
			sauterSymCour("finpour");
			return (new NoeudPour(ls.getLigne(), ls.getColonne(), aff, nExpEt, incr, nSeqInst));
		} else {
			erreur("<finpour>");
			return NULL;
		}
		
	} else {
		erreur("<instPour>");
		return NULL;
	}

}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire() {
// <instLire> ::= lire ( <variable> )
	sauterSymCour("lire");

	if (ls.getSymCour()=="(") {
		ls.suivant();
		Noeud* var = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
		sauterSymCour("<VARIABLE>");
		sauterSymCour(")");
		return (new NoeudLire(ls.getLigne(), ls.getColonne(), var));
	} else {
		erreur("<instLire>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire() {
// <instEcrire> ::= ecrire ( <expression> | <chaine> )

	sauterSymCour("ecrire");

	if (ls.getSymCour()=="(") {
		ls.suivant();
		Noeud* var = NULL;
		bool chaine;
		if(ls.getSymCour() == "<CHAINE>"){
			var = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
			ls.suivant();
			chaine = true;
		} else {
			var = expression();
			chaine = false;
		}
		sauterSymCour(")");
		return (new NoeudEcrire(ls.getLigne(), ls.getColonne(), chaine, var));
	} else {
		erreur("<instEcrire>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instChoisir() {
// <instChoisir> := choisir (<variable>) { cas <variable> | <entier> : <seqInst> } defaut : <seqInst> finchoisir
	if(ls.getSymCour() == "choisir") {
		sauterSymCour("choisir");
		sauterSymCour("(");
		Noeud* choisir = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
		sauterSymCour("<VARIABLE>");
		sauterSymCour(")");

		if(ls.getSymCour() == "cas") {
			sauterSymCour("cas");
			Noeud* cas = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
			ls.suivant();
			sauterSymCour(":");

			Noeud * nExpSeq = seqInst();

			Noeud * suivant = NULL;

			if(ls.getSymCour() == "cas") {
				suivant = instCas();
				return (new NoeudChoisir(ls.getLigne(), ls.getColonne(), choisir, cas, nExpSeq, suivant));
			} else if (ls.getSymCour() == "defaut") {
				sauterSymCour("defaut");
				sauterSymCour(":");
				suivant = seqInst();
			}
		
			if(ls.getSymCour()=="finchoisir"){
				sauterSymCour("finchoisir");
				return (new NoeudChoisir(ls.getLigne(), ls.getColonne(), choisir, cas, nExpSeq, suivant));
			} else {
				erreur("<instChoisir>");
				return NULL;
			}

		} else {
			erreur("<instChoisir>");
			return NULL;
		}

	} else {
		erreur("<instChoisir>");
		return NULL;
	}

}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instCas() {
	if(ls.getSymCour() == "cas") {
		sauterSymCour("cas");
		Noeud* cas = ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
		ls.suivant();
		sauterSymCour(":");

		Noeud * nExpSeq = seqInst();

		Noeud * suivant = NULL;

		if(ls.getSymCour() == "cas") {
			suivant = instCas();
			return (new NoeudCas(ls.getLigne(), ls.getColonne(), cas, nExpSeq, suivant));
		} else if (ls.getSymCour() == "defaut") {
			sauterSymCour("defaut");
			sauterSymCour(":");
			suivant = seqInst();
		}
		
		if(ls.getSymCour()=="finchoisir"){
			sauterSymCour("finchoisir");
			return (new NoeudCas(ls.getLigne(), ls.getColonne(), cas, nExpSeq, suivant));
		} else {
			erreur("<instChoisir>");
			return NULL;
		}
	} else {
		erreur("<instChoisir>");
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }
	Noeud* term = terme();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* termDroit=terme(); // lecture de l'operande droit
		term = new NoeudOperateurBinaire(ls.getLigne(), ls.getColonne(), operateur,term,termDroit); // const. du noeud
	}
	return term;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme() {
// <terme> ::= <facteur> { <opMult> <facteur> }
	Noeud* fact = facteur();
	while (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
		Symbole operateur = opMult(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=facteur(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(ls.getLigne(), ls.getColonne(), operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expEt()
// <expEt> ::= <expOu> { <opEt> <expOu> }
{
	Noeud* nExpOu = expOu();
	while (ls.getSymCour()=="et") {
		Symbole operateur = opEt();
		Noeud* nExpOuDroit = expOu();
		nExpOu = new NoeudOperateurBinaire(ls.getLigne(), ls.getColonne(), operateur, nExpOu, nExpOuDroit);
	}
	return nExpOu;
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expOu()
// <expOu> ::= <relation> { <opOu> <relation> }
{
	Noeud* rel = relation();
	while (ls.getSymCour()=="ou") {
		Symbole operateur = opOu();
		Noeud* nRelDroit = relation();
		rel = new NoeudOperateurBinaire(ls.getLigne(), ls.getColonne(), operateur, rel, nRelDroit);
	}
	return rel;
}

Noeud* LecteurPhraseAvecArbre::relation()
// <relation> ::= <expression> { <opRel> <expression> }
{
	Noeud* exp = expression();
	while(ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" || ls.getSymCour()=="<=" || ls.getSymCour()==">" || ls.getSymCour()==">=") {
		Symbole operateur = opRel();
		Noeud* nExpDroit = expression();
		exp = new NoeudOperateurBinaire(ls.getLigne(), ls.getColonne(), operateur, exp, nExpDroit);
	}
	return exp;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier> | <chaine> | <variable> | <opUnaire> <expEt> | ( <expEt> )

	Noeud* fact=NULL;
	Symbole operateur;
	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>" || ls.getSymCour()=="<CHAINE>") {
		fact=ts.chercheAjoute(ls.getLigne(), ls.getColonne(), ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-" || ls.getSymCour()=="non") {
		//ls.suivant();
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
		//fact = new NoeudOperateurAdd(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
		operateur = opUnaire();
		fact = new NoeudOperateurUnaire(ls.getLigne(), ls.getColonne(), operateur, expEt());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expEt();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd() {
// <opAdd> ::= + | -
	Symbole operateur;
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opAdd>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult() {
// <opAdd> ::= * | /
	Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opMult>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire() {
// <opUnaire> ::= - | non
	Symbole operateur;
	if (ls.getSymCour()=="-" || ls.getSymCour()=="non") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opUnaire>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel()
// <opRel> ::= == | != | < | <= | > | >=
{
	Symbole operateur;
	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" || ls.getSymCour()=="<=" || ls.getSymCour()==">" || ls.getSymCour()==">=") {
		operateur=ls.getSymCour();
		ls.suivant();
	} else {
		erreur("<opRel>");
	}
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt() {
// <opEt> ::= et
	Symbole operateur;
	if (ls.getSymCour()=="et") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opEt>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu() {
// <opOu> ::= ou
	Symbole operateur;
	if (ls.getSymCour()=="ou") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opOu>");
	return operateur;
}


////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		throw ErreurSyntaxique("Erreur syntaxique : ", ls.getLigne(), ls.getColonne(), ch, ls.getSymCour());
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	throw ErreurSyntaxique("Erreur syntaxique : ", ls.getLigne(), ls.getColonne(), mess, ls.getSymCour());
}
